`timescale 1ns / 1ps

// Total Latency = 5
module norm_actv_datapath_lut
#(
    parameter BIT_I     = 16,
    parameter BIT_B     = 16,
    parameter BIT_M     = 16,
    parameter BIT_O     = 8,
    parameter RSHIFT    = 16
)
(
    input   clk,
    input   clken,
    
    input   [BIT_M+BIT_B-1 : 0]    norm_in,
    input   [      BIT_I-1 : 0]    data_in,
    output  [      BIT_O-1 : 0]    data_out
);

localparam BIT_ADD = BIT_I + 1;
localparam BIT_MUL = BIT_ADD + BIT_M;
localparam BIT_RSH = BIT_MUL - RSHIFT;
localparam ACTV_MIN = { BIT_O{1'b0} };
localparam ACTV_MAX = { BIT_O{1'b1} };

reg     [BIT_I-1    : 0]    data_pe;
reg     [BIT_B-1    : 0]    bias_in;
reg     [BIT_M-1    : 0]    mult_in;

reg     [BIT_ADD-1  : 0]    add_pipe;
reg     [BIT_M-1    : 0]    m_pipe;

wire    [BIT_MUL-1  : 0]    mul_out;
wire    [BIT_RSH-1  : 0]    rsh_out;

reg     [BIT_O-1    : 0]    clip_pipe;
reg     [BIT_O-1    : 0]    clip_out;

assign  data_out = clip_pipe;
assign  rsh_out = mul_out[BIT_MUL-1 : RSHIFT];

mul_s17_s16_p2 inst_mul
(
    .CLK    (clk),          // input wire CLK
    .A      (add_pipe),     // input wire [16 : 0] A
    .B      (m_pipe),       // input wire [15 : 0] B
    .CE     (clken),        // input wire CE
    .P      (mul_out)       // output wire [32 : 0] P
);

always @(posedge clk)
begin
    if (clken)
    begin
        // stage 0: input pipe
        data_pe <= data_in;
        bias_in <= norm_in[      BIT_B-1 : 0];
        mult_in <= norm_in[BIT_M+BIT_B-1 : BIT_B];

        // stage 1: add
        add_pipe <= $signed(data_pe) + $signed(bias_in);    // 16 bit adder
        m_pipe   <= mult_in;

        // stage 2: multiply, latency=2
        // ......

        // stage 3: right shift & clip
        clip_pipe <= clip_out;
    end
end

always @(*)
begin
    if ($signed(rsh_out) < $signed(ACTV_MIN))
    begin
        clip_out = ACTV_MIN;
    end
    else if ($signed(rsh_out) > ACTV_MAX)
    begin
        clip_out = ACTV_MAX;
    end
    else begin
        clip_out = rsh_out[BIT_O-1 : 0];
    end
end

endmodule


// Total Latency = 5
module norm_actv_datapath_dsp
#(
    parameter BIT_I     = 16,
    parameter BIT_B     = 16,
    parameter BIT_M     = 16,
    parameter BIT_O     = 8,
    parameter RSHIFT    = 16
)
(
    input   clk,
    input   clken,
    
    input   [BIT_M+BIT_B-1 : 0]    norm_in,
    input   [      BIT_I-1 : 0]    data_in,
    output  [      BIT_O-1 : 0]    data_out
);

localparam BIT_ADD = BIT_I + 1;
localparam BIT_MUL = BIT_ADD + BIT_M;
localparam BIT_RSH = BIT_MUL - RSHIFT;
localparam ACTV_MIN = { BIT_O{1'b0} };
localparam ACTV_MAX = { BIT_O{1'b1} };

wire    [BIT_B-1    : 0]    bias_in;
wire    [BIT_M-1    : 0]    mult_in;

wire    [BIT_MUL-1  : 0]    mul_out;
wire    [BIT_RSH-1  : 0]    rsh_out;

reg     [BIT_O-1    : 0]    clip_pipe;
reg     [BIT_O-1    : 0]    clip_out;

assign  data_out = clip_pipe;

assign bias_in = norm_in[      BIT_B-1 : 0];
assign mult_in = norm_in[BIT_M+BIT_B-1 : BIT_B];

assign  rsh_out = mul_out[BIT_MUL-1 : RSHIFT];

dsp_norm inst_dsp (
    .CLK    (clk),      // input wire CLK
    .CE     (clken),    // input wire CE
    .A      (bias_in),  // input wire [15 : 0] A
    .B      (mult_in),  // input wire [15 : 0] B
    .D      (data_in),  // input wire [15 : 0] D
    .P      (mul_out)   // output wire [32 : 0] P
);

always @(posedge clk)
begin
    if (clken)
    begin
        // stage 3: right shift & clip
        clip_pipe <= clip_out;
    end
end

always @(*)
begin
    if ($signed(rsh_out) < $signed(ACTV_MIN))
    begin
        clip_out = ACTV_MIN;
    end
    else if ($signed(rsh_out) > ACTV_MAX)
    begin
        clip_out = ACTV_MAX;
    end
    else begin
        clip_out = rsh_out[BIT_O-1 : 0];
    end
end

endmodule
